package core

import (
	"embed"
	"time"
	"math/big"
	"kumachan/standalone/ctn"
)


//go:embed builtin.km
var builtinFileSystem embed.FS
func BuiltinFileSystem() embed.FS { return builtinFileSystem }
func BuiltinFileName() string { return "builtin.km" }

const NoneAndErrorIndex = 0
const OkIndex = 1

const ( T_Bool = "Bool"; TrueString = "Yes"; FalseString = "No" )
const T_None = "None"
const T_Maybe = "Maybe"
const T_Error = "Error"
const T_Int = "Int"
const T_Float = "Float"
const T_Char = "Char"
const T_String = "String"
const T_Bytes = "Bytes"
const T_List = "List"
const T_Queue = "Queue"
const T_Heap = "Heap"
const T_Set = "Set"
const T_Map = "Map"
const T_Observable = "$"
const T_Lambda = "Lambda"
const T_Pair = "Pair"
const T_Triple = "Triple"
const T_Time = "Time"
const T_Asset = "Asset"
const T_Lens1 = "Lens1"
const T_Lens2 = "Lens2"

type lens1 struct {
	value   Object
	update  func(Object)(Object)
}
type lens2 struct {
	value   ctn.Optional[Object]
	update  func(Object)(Object)
}

// TODO: add and use more utility functions
// TODO: move utility functions to `interop.go`
func ObjInt(n int) Object {
	return ObjIntFromInt64(int64(n))
}
func ObjIntFromInt64(n int64) Object {
	return Obj(Int { big.NewInt(n) })
}
func ObjFloat(x float64) Object {
	return Obj(Float(x))
}
func ObjString(s string) Object {
	return Obj(String(s))
}
func ObjPair(a Object, b Object) Object {
	return Obj(Record { Objects: [] Object { a, b } })
}
func ObjList(l ([] Object)) Object {
	var buf ListBuilder
	for _, item := range l {
		buf.Append(item)
	}
	return Obj(buf.Collect())
}
func ObjQueue(q ctn.Queue[Object]) Object {
	return Obj(Queue(q))
}
func ObjTime(t time.Time) Object {
	return Obj(Time(t))
}
func ObjTimeNow() Object {
	return ObjTime(time.Now())
}
func GetBool(o Object) bool {
	return bool((*o).(Bool))
}
func GetInt(o Object) int {
	// note: undefined value returned when overflow
	return int((*o).(Int).Value.Int64())
}
func GetPair(o Object) (Object, Object) {
	return FromObject[ctn.Pair[Object,Object]](o)()
}
func GetList(o Object) List {
	return (*o).(List)
}
func GetQueue(o Object) Queue {
	return (*o).(Queue)
}
func GetObservable(o Object) Observable {
	return (*o).(Observable)
}
func YieldObservables(o ...Observable) func(func(Observable)) {
	return func(yield func(Observable)) {
		for _, o := range o {
			yield(o)
		}
	}
}
func ListToPair(o Object) Object {
	var l = GetList(o)
	a, l, ok1 := l.Shift()
	b, l, ok2 := l.Shift()
	if !(ok1 && ok2) { panic("invalid argument") }
	return ObjPair(a, b)
}
func QueueToPair(o Object) Object {
	var q = ctn.Queue[Object](GetQueue(o))
	a, q, ok1 := q.Shifted()
	b, q, ok2 := q.Shifted()
	if !(ok1 && ok2) { panic("invalid argument") }
	return ObjPair(a, b)
}
func UnwrapMaybe(o Object) (Object, bool) {
	var u = (*o).(Union)
	if u.Index == NoneAndErrorIndex {
		return nil, false
	} else {
		return u.Object, true
	}
}
func UnwrapLens2(o Object) (Object, bool) {
	var l = FromObject[lens2](o)
	return l.value.Value()
}


